package cn.yshujia.repository;

import cn.yshujia.domain.dto.PageDTO;
import cn.yshujia.domain.entity.*;
import cn.yshujia.domain.enums.ClassifyEnum;
import cn.yshujia.domain.vo.*;
import cn.yshujia.mapper.ArticleMapper;
import cn.yshujia.mapper.CommentMapper;
import cn.yshujia.mapper.LabelMapper;
import cn.yshujia.mapper.LikeMapper;
import cn.yshujia.transform.ArticleTransform;
import cn.yshujia.utils.PageUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Repository;
import org.springframework.util.ObjectUtils;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author YShuJia
 * @create 2024/11/28
 * @description ArticleRepository
 */

@Repository
public class ArticleRepository {
	
	@Resource
	RedisTemplate<String, ArticleVO> redis;
	
	@Resource
	RedisTemplate<String, ArchiveVO> redisArchive;
	
	@Resource
	ArticleMapper mapper;
	
	@Resource
	ClassifyRepository classifyRepository;
	
	@Resource
	LabelRepository labelRepository;
	
	@Resource
	LabelMapper labelMapper;
	
	@Resource
	ResourcesRepository resourcesRepository;
	
	@Resource
	LikeMapper likeMapper;
	
	@Resource
	CommentMapper commentedMapper;
	
	private static final String VO = "article:";
	// 文章列表根据分类查询
	private static final String VO_LABEL = "article:label:";
	
	private static final String VO_ARCHIVE = "article:archive";
	// 文章列表根据标签查询
	private static final String VO_RECOMMEND = "article:recommend";
	
	private static final String VO_DEPLOY = "article:deploy";
	
	private static final Duration DURATION = Duration.ofDays(1L);
	
	// UI 分页查询
	public PageVO<ArticleVO> pageByLabelId(PageDTO dto) {
		Map<String, Object> param = dto.getParam();
		
		if (ObjectUtils.isEmpty(param) || ObjectUtils.isEmpty(param.get("labelId"))) {
			return new PageVO<>();
		}
		long labelId = Long.parseLong(param.get("labelId").toString());
		// 判断缓存中是否存在
		long start = ((long) (dto.getCurrent() - 1) * dto.getSize());
		List<ArticleVO> list = redis.opsForList().range(VO_LABEL + labelId, start, start + dto.getSize() - 1);
		if (!ObjectUtils.isEmpty(list)) {
			return PageUtils.getPage(dto, list, redis.opsForList().size(VO_LABEL + labelId));
		}
		
		list = new ArrayList<>();
		// 获取数据库数据
		LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(Article::getStatus, true)
				.eq(Article::getLabelId, labelId)
				.orderByDesc(Article::getCreateTime);
		List<Article> sql = mapper.selectList(wrapper);
		if (!ObjectUtils.isEmpty(sql)) {
			for (Article article : sql) {
				ArticleVO vo = createArticleVO(article);
				// 获取评论数量
				vo.setCommentCount(commentedMapper.selectCount(new LambdaQueryWrapper<Comment>()
						.eq(Comment::getSourceId, article.getId())));
				// 设置标签
				vo.setLabel(labelRepository.get(article.getLabelId()));
				list.add(vo);
			}
			redis.opsForList().rightPushAll(VO_LABEL + labelId, list);
			redis.opsForList().getOperations().expire(VO_LABEL + labelId, DURATION);
		}
		return PageUtils.getPage(dto, list, Long.parseLong(list.size() + ""));
	}
	
	public List<ArchiveVO> selectArchiveList() {
		List<ArchiveVO> voList = redisArchive.opsForList().range(VO_ARCHIVE, 0L, -1L);
		if (!ObjectUtils.isEmpty(voList)) {
			return voList;
		}
		voList = new ArrayList<>();
		List<Classify> list = classifyRepository.selectListByType(ClassifyEnum.ARTICLE.getType());
		if (ObjectUtils.isEmpty(list)) {
			return new ArrayList<>();
		}
		for (Classify classifyVO : list) {
			ArchiveVO archiveVO = new ArchiveVO();
			archiveVO.setClassifyId(classifyVO.getId());
			archiveVO.setClassifyName(classifyVO.getName());
			List<ArticleLabelVO> labelList = mapper.selectArticleLabelVOList(classifyVO.getId());
			// 设置标签随机图片
			if (!ObjectUtils.isEmpty(labelList)) {
				for (ArticleLabelVO articleLabelVO : labelList) {
					ResourcesVO resourcesVO = resourcesRepository.selectRandomWall();
					if (!ObjectUtils.isEmpty(resourcesVO)) {
						articleLabelVO.setImgUrl(resourcesVO.getUrl());
					}
				}
			}
			archiveVO.setLabelList(labelList);
			voList.add(archiveVO);
		}
		redisArchive.opsForList().rightPushAll(VO_ARCHIVE, voList);
		redisArchive.opsForList().getOperations().expire(VO_ARCHIVE, DURATION);
		return voList;
	}
	
	public List<ArticleVO> selectRecommendList() {
		List<ArticleVO> voList = redis.opsForList().range(VO_RECOMMEND, 0L, -1L);
		if (!ObjectUtils.isEmpty(voList)) {
			return voList;
		}
		List<Article> list = mapper.selectList(new LambdaQueryWrapper<Article>()
				.eq(Article::getStatus, true)
				.eq(Article::getIsRecommend, true)
				.orderByDesc(Article::getViewCount)
				.last("limit 10"));
		if (!ObjectUtils.isEmpty(list)) {
			voList = new ArrayList<>(list.size());
			for (Article article : list) {
				ArticleVO vo = createArticleVO(article);
				vo.setCommentCount(commentedMapper.selectCount(new LambdaQueryWrapper<Comment>()
						.eq(Comment::getSourceId, article.getId())));
				voList.add(vo);
			}
			redis.opsForList().rightPushAll(VO_RECOMMEND, voList);
			redis.opsForList().getOperations().expire(VO_RECOMMEND, DURATION);
		}
		return voList;
	}
	
	public List<ArticleVO> selectDeployList() {
		List<ArticleVO> voList = redis.opsForList().range(VO_DEPLOY, 0L, -1L);
		if (!ObjectUtils.isEmpty(voList)) {
			return voList;
		}
		List<Classify> classifies = classifyRepository.selectListByType(ClassifyEnum.DEPLOY.getType());
		if (ObjectUtils.isEmpty(classifies)) {
			return new ArrayList<>();
		}
		// 获取所有分类id
		List<Long> ids = classifies.stream().map(Classify::getId).toList();
		
		List<Label> labelList = labelMapper.selectList(new LambdaQueryWrapper<Label>()
				.in(Label::getClassifyId, ids)
				.eq(Label::getStatus, true));
		
		List<Long> labelIds = labelList.stream().map(Label::getId).toList();
		
		if (ObjectUtils.isEmpty(labelIds)) {
			return new ArrayList<>();
		}
		// 获取所有文章
		List<Article> list = mapper.selectList(new LambdaQueryWrapper<Article>()
				.in(Article::getLabelId, labelIds)
				.eq(Article::getStatus, true));
		return createVOList(list);
	}
	
	public ArticleVO get(Long id) {
		updateViewCount(id);
		ArticleVO articleVO = redis.opsForValue().get(VO + id);
		if (!ObjectUtils.isEmpty(articleVO)) {
			return articleVO;
		}
		Article article = mapper.selectById(id);
		if (!ObjectUtils.isEmpty(article)) {
			articleVO = createArticleVO(article);
			articleVO.setLabel(labelRepository.get(article.getLabelId()));
			redis.opsForValue().set(VO + id, articleVO, DURATION);
		}
		return articleVO;
	}
	
	public List<ArticleVO> createVOList(List<Article> list) {
		if (ObjectUtils.isEmpty(list)) {
			return new ArrayList<>();
		}
		List<ArticleVO> voList = new ArrayList<>(list.size());
		for (Article article : list) {
			ArticleVO vo = createArticleVO(article);
			voList.add(vo);
		}
		return voList;
	}
	
	public ArticleVO createArticleVO(Article article) {
		ArticleVO articleVO = ArticleTransform.entity2VO(article);
		LabelVO labelVO = labelRepository.get(article.getLabelId());
		if (null == labelVO) {
			return articleVO;
		}
		articleVO.setLabel(labelVO);
		articleVO.setCommentCount(commentedMapper.selectCount(new LambdaQueryWrapper<Comment>()
				.eq(Comment::getSourceId, article.getId())));
		return articleVO;
	}
	
	@Async ("Task")
	protected void updateViewCount(Long id) {
		Article article = mapper.selectById(id);
		if (article != null) {
			article.setViewCount(article.getViewCount() + 1);
			mapper.updateById(article);
		}
	}
	
	@Async ("Task")
	public void del() {
		Set<String> keys = redis.keys(VO + "*");
		redis.delete(keys);
	}
	
	/**
	 * [] * @return void
	 *
	 * @author YShuJia
	 * @description 每天的凌晨 1点 同步一次文章的点赞数量
	 * @create 2024/12/20 14:55
	 */
	@Async ("Task")
	@Scheduled (cron = "0 0 1 * * ?")
	public void update() {
		del();
		List<Article> articles = mapper.selectList(new LambdaQueryWrapper<Article>()
				.select(Article::getId));
		for (Article article : articles) {
			Long count = likeMapper.selectCount(new LambdaQueryWrapper<Like>()
					.eq(Like::getSourceId, article.getId())
					.eq(Like::getStatus, true));
			article.setLikeCount(Integer.parseInt(count.toString()));
			mapper.updateById(article);
		}
	}
	
	@PostConstruct
	public void init() {
		update();
	}
}
